#include <avformat.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
/**
 *
 * @return
 */
int formatDemo(const char* filePath, enum file_type type){
    //av_register_all();
    AVFormatContext * pContext = NULL;

    //1
    AVDictionary* opt = NULL;
    if(type == NET){
        avformat_network_init();
        av_dict_set(&opt, "rtsp_transport", "tcp", 0);
        av_dict_set(&opt, "max_delay", "550", 0);
    }

    //打开输入流
    int ret = avformat_open_input(&pContext, filePath, NULL, &opt);
    if(ret){
        av_log(NULL, AV_LOG_INFO, "%s:%s", "[ERROR]=>avformat_open_input error!\n", filePath);
        return 1;
    }
    av_log(NULL, AV_LOG_INFO, "avformat_open_input success!\n");

    //2 寻找流信息 H264 宽高
    ret = avformat_find_stream_info(pContext, NULL);
    if(ret){
        av_log(NULL, AV_LOG_ERROR, "%s:%s", "[ERROR]=>avformat_find_stream_info error!\n", filePath);
        return 1;
    }
    av_log(NULL, AV_LOG_INFO, "avformat_find_stream_info success!\n");

    int time = pContext->duration;
    int min = (time / 1000000) / 60; //分钟
    int sec = (time / 1000000) % 60; //秒
    av_log(NULL, AV_LOG_INFO, "%d分%d秒\n", min, sec);
    //显示一下
    av_dump_format(pContext, 0, filePath, 0);

    //寻找流
    int videoStream = -1, audioStream = -1;
    //查找视频流
    videoStream = av_find_best_stream(pContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
   // audioStream = av_find_best_stream(pContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

   //找解码器
    AVCodec* vCodec = avcodec_find_decoder(pContext->streams[videoStream]->codecpar->codec_id);
    if(!vCodec){
        av_log(NULL, AV_LOG_ERROR, "%s:%s\n", "[ERROR]=>avcodec_find_decoder error!", filePath);
        return -1;
    }
    av_log(NULL, AV_LOG_INFO, "%s\n", "[INFO]=>avcodec_find_decoder success!");

    AVCodecContext* avCodecContext = avcodec_alloc_context3(vCodec);
    avcodec_parameters_to_context(avCodecContext, pContext->streams[videoStream]->codecpar);


    //打开解码器
    ret = avcodec_open2(avCodecContext, vCodec,  NULL);
    if(ret != 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "[ERROR]=>avcodec_open2 error!");
        return -1;
    }
    av_log(NULL, AV_LOG_INFO, "%s\n", "[INFO]=>avcodec_open2 success!");

    //****计算输入流信息****//
    AVRational frame_rate = av_guess_frame_rate(pContext, pContext->streams[videoStream], NULL); //帧率
    av_log(NULL, AV_LOG_INFO, "帧率:%d/%d\n", frame_rate.num, frame_rate.den);


    //开辟一块内存空间
    AVFrame* frame = av_frame_alloc();
    //创建一个yuv420视频像素数据格式缓冲区(一帧数据)
    AVFrame* avFrame_yuv420p = av_frame_alloc();

    //分配空间，进行图像转换
    int width = pContext->streams[videoStream]->codecpar->width;
    int height = pContext->streams[videoStream]->codecpar->height;
    int format = pContext->streams[videoStream]->codecpar->format;

    int nSize = av_image_get_buffer_size((enum AVPixelFormat)format, width, height, 1); //通过指定像素格式、图像宽、图像高来计算所需的内存大小
    av_log(NULL, AV_LOG_INFO, "空间大小:%d\n", nSize);
    //开辟一块内存空间
    uint8_t * buf = (uint8_t*)av_malloc(nSize);
   // 向avFrame_yuv420p->填充数据
    //参数一：目标->填充数据(avframe_yuv420p)
   //参数二：目标->每一行大小
   //参数三：原始数据
   //参数四：目标->格式类型
   //参数五：宽
   //参数六：高
   //参数七：字节对齐方式
    av_image_fill_arrays(avFrame_yuv420p->data,
                         avFrame_yuv420p->linesize,
                         buf,
    AV_PIX_FMT_YUV420P,
    avCodecContext->width,
    avCodecContext->height,
    1);

    AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));

    //转换上下文
    struct SwsContext * swsContext = sws_getContext(width, height, (enum AVPixelFormat)format,
            width, height, AV_PIX_FMT_YUV420P, SWS_BICUBIC,
            NULL, NULL, NULL);

   // 将yuv420p数据写入.yuv文件中
    const char *outfile = "mp4.yuv";
    FILE* file_yuv420p = fopen(outfile,"wb+");
    if (file_yuv420p ==NULL){
        av_log(NULL, AV_LOG_ERROR, "%s\n:%s", "输出文件打开失败", outfile);
        return 1;
    }
    char errBuf[1024];
    //读帧
    int frameCount = 0;
    int y_size, u_size, v_size;
    while ( av_read_frame(pContext, packet) >= 0){ //读到了
        //判断stream_index
        if(packet->stream_index == AVMEDIA_TYPE_VIDEO){
            //解码
            //解码一帧压缩数据->得到视频像素数据->yuv格式
            //发送一帧视频压缩数据,发送数据到ffmepg，放到解码队列中
            ret = avcodec_send_packet(avCodecContext, packet);
            if(ret != 0){
                av_log(NULL, AV_LOG_ERROR, "%s\n", "[ERROR]=>avcodec_send_packet error!");
                return -1;
            }
            //将成功的解码队列中取出1个frame
            ret = avcodec_receive_frame(avCodecContext, frame);
            if(ret != 0){
                if(ret == AVERROR(EAGAIN)){
                    continue;
                }
                av_strerror(ret, errBuf, 1024);
                av_log(NULL, AV_LOG_ERROR, "%s:%d:%s\n", "[ERROR]=>avcodec_receive_frame error!",ret, errBuf);
                return -1;
            }
            //解码成功
            //不能够保证解码出来的一帧视频像素数据格式是yuv格式
            //视频像素数据格式很多种类型: yuv420P、yuv422p、yuv444p等等...
            //保证：我的解码后的视频像素数据格式统一为yuv420P->通用的格式
            //进行类型转换: 将解码出来的视频像素点数据格式->统一转类型为yuv420P
            //sws_scale作用：进行类型转换的
            //参数一：视频像素数据格式上下文
            //参数二：原来的视频像素数据格式->输入数据
            //参数三：原来的视频像素数据格式->输入画面每一行大小
            //参数四：原来的视频像素数据格式->输入画面每一行开始位置(填写：0->表示从原点开始读取)
            //参数五：原来的视频像素数据格式->输入数据行数
            //参数六：转换类型后视频像素数据格式->输出数据
            //参数七：转换类型后视频像素数据格式->输出画面每一行大小
            sws_scale(swsContext, (const uint8_t *const *)frame->data,
                      frame->linesize, 0, avCodecContext->height,
                      avFrame_yuv420p->data, avFrame_yuv420p->linesize);
            //方式一：直接显示视频上面去

            //方式二：写入yuv文件格式
            //YUV420P格式规范一：Y结构表示一个像素(一个像素对应一个Y)
            //YUV420P格式规范二：4个像素点对应一个(U和V: 4Y = U = V)
            y_size = avCodecContext->width * avCodecContext->height;
            u_size = y_size/4;
            v_size = y_size/4;
            //首先->Y数据
            fwrite(avFrame_yuv420p->data[0], 1, y_size, file_yuv420p);
            //其次->U数据
            fwrite(avFrame_yuv420p->data[1], 1, u_size, file_yuv420p);
            //再其次->V数据
            fwrite(avFrame_yuv420p->data[2], 1, v_size, file_yuv420p);

             //av_log(NULL, AV_LOG_INFO, "当前解码第%d帧\n", frameCount++);
        }
    }

    //释放内存资源，关闭解码器
    av_packet_free(&packet);
    fclose(file_yuv420p);
    av_frame_free(&frame);
    av_frame_free(&avFrame_yuv420p);
    free(buf);
    avcodec_close(avCodecContext);
    avformat_free_context(pContext);
    return 0;
}